<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>第1章 Vue 框架介绍 | 深入理解Vue.js实战</title>
    <meta name="description" content="作者：被删">
    
    
    <link rel="preload" href="/vue-ebook/assets/css/0.styles.b08616b1.css" as="style"><link rel="preload" href="/vue-ebook/assets/js/app.0a4e61a5.js" as="script"><link rel="preload" href="/vue-ebook/assets/js/3.846561a8.js" as="script"><link rel="preload" href="/vue-ebook/assets/js/2.eed49fdd.js" as="script"><link rel="preload" href="/vue-ebook/assets/js/8.0e937857.js" as="script">
    <link rel="stylesheet" href="/vue-ebook/assets/css/0.styles.b08616b1.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/vue-ebook/" class="home-link router-link-active"><!----> <span class="site-name">深入理解Vue.js实战</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/vue-ebook/" class="nav-link">概述</a></div><div class="nav-item"><a href="/vue-ebook/vue-ebook/" class="nav-link router-link-active">内容</a></div> <a href="https://github.com/godbasin/vue-ebook" target="_blank" rel="noopener noreferrer" class="repo-link">
    Github
    <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><nav class="nav-links"><div class="nav-item"><a href="/vue-ebook/" class="nav-link">概述</a></div><div class="nav-item"><a href="/vue-ebook/vue-ebook/" class="nav-link router-link-active">内容</a></div> <a href="https://github.com/godbasin/vue-ebook" target="_blank" rel="noopener noreferrer" class="repo-link">
    Github
    <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></nav>  <ul class="sidebar-links"><li><section class="sidebar-group depth-0" style="padding-top:;"><!----> <p class="sidebar-heading"><span>前言</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/vue-ebook/vue-ebook/0.html" class="sidebar-link">前端框架的出现</a></li></ul></section></li><li><section class="sidebar-group depth-0" style="padding-top:10px;"><div class="kitty-main" data-v-0cd75e06><span class="stand" data-v-0cd75e06></span> <div class="cat" data-v-0cd75e06><div class="body" data-v-0cd75e06></div> <div class="head" data-v-0cd75e06><div class="ear" data-v-0cd75e06></div> <div class="ear" data-v-0cd75e06></div></div> <div class="face" data-v-0cd75e06><div class="nose" data-v-0cd75e06></div> <div class="whisker-container" data-v-0cd75e06><div class="whisker" data-v-0cd75e06></div> <div class="whisker" data-v-0cd75e06></div></div> <div class="whisker-container" data-v-0cd75e06><div class="whisker" data-v-0cd75e06></div> <div class="whisker" data-v-0cd75e06></div></div></div> <div class="tail-container" data-v-0cd75e06><div class="tail" data-v-0cd75e06><div class="tail" data-v-0cd75e06><div class="tail" data-v-0cd75e06><div class="tail" data-v-0cd75e06><div class="tail" data-v-0cd75e06><div class="tail" data-v-0cd75e06><div class="tail" data-v-0cd75e06></div></div></div></div></div></div></div></div></div></div> <p class="sidebar-heading open"><span>第一部分 Vue快速入门</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/vue-ebook/vue-ebook/1.html" class="active sidebar-link">第1章 Vue 框架介绍</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/vue-ebook/vue-ebook/1.html#_1-1-解析语法生成-ast" class="sidebar-link">1.1 解析语法生成 AST</a></li><li class="sidebar-sub-header"><a href="/vue-ebook/vue-ebook/1.html#_1-2-虚拟-dom" class="sidebar-link">1.2 虚拟 DOM</a></li><li class="sidebar-sub-header"><a href="/vue-ebook/vue-ebook/1.html#_1-3-数据绑定" class="sidebar-link">1.3 数据绑定</a></li><li class="sidebar-sub-header"><a href="/vue-ebook/vue-ebook/1.html#_1-4-xss-漏洞填补" class="sidebar-link">1.4 XSS 漏洞填补</a></li></ul></li><li><a href="/vue-ebook/vue-ebook/2.html" class="sidebar-link">第2章 Vue 环境快速搭建</a></li><li><a href="/vue-ebook/vue-ebook/3.html" class="sidebar-link">第3章 Vue 基础介绍</a></li><li><a href="/vue-ebook/vue-ebook/4.html" class="sidebar-link">第4章 Vue 组件的使用</a></li><li><a href="/vue-ebook/vue-ebook/5.html" class="sidebar-link">第5章 常用指令和自定义指令</a></li><li><a href="/vue-ebook/vue-ebook/6.html" class="sidebar-link">第6章 Vue 动画</a></li><li><a href="/vue-ebook/vue-ebook/7.html" class="sidebar-link">第7章 Vue Router 路由搭建应用</a></li><li><a href="/vue-ebook/vue-ebook/8.html" class="sidebar-link">第8章 实战：Todo List 从组件到应用</a></li></ul></section></li><li><section class="sidebar-group depth-0" style="padding-top:;"><!----> <p class="sidebar-heading"><span>第二部分 Vue的正确使用方式</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/vue-ebook/vue-ebook/9.html" class="sidebar-link">第9章 思维转变与大型项目管理</a></li><li><a href="/vue-ebook/vue-ebook/10.html" class="sidebar-link">第10章 如何正确地进行抽象</a></li><li><a href="/vue-ebook/vue-ebook/11.html" class="sidebar-link">第11章 全局数据管理与 Vuex</a></li><li><a href="/vue-ebook/vue-ebook/12.html" class="sidebar-link">第12章 实战：三天开发一个管理端</a></li><li><a href="/vue-ebook/vue-ebook/13.html" class="sidebar-link">第13章 实战：表单配置化实现</a></li><li><a href="/vue-ebook/vue-ebook/14.html" class="sidebar-link">第14章 实战：使用 Webpack 或 Vue CLI 搭建多页应用</a></li><li><a href="/vue-ebook/vue-ebook/15.html" class="sidebar-link">第15章 Vue 周边拓展</a></li><li><a href="/vue-ebook/vue-ebook/16.html" class="sidebar-link">第16章 关于 Vue 3.0</a></li></ul></section></li><li><section class="sidebar-group depth-0" style="padding-top:;"><!----> <p class="sidebar-heading"><span>后记</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/vue-ebook/vue-ebook/99.html" class="sidebar-link">关于框架选型</a></li></ul></section></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><h1 id="第1章-vue-框架介绍"><a href="#第1章-vue-框架介绍" aria-hidden="true" class="header-anchor">#</a> 第1章 Vue 框架介绍</h1> <p>根据 Vue 官方的介绍，Vue 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是，Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层，不仅易于上手，还便于与第三方库或既有项目整合。但 Vue 并不只是一个简单的视图库，通过与一系列周边工具的配合，它也可以轻易地构建大型应用。</p> <p>所以 Vue 框架到底做了些什么呢？为什么几乎大多数前端都基本需要掌握呢？前言里也讲到了 jQuery 一把梭的时代，而大多数使用过 jQuery 的非前端人员，都有种“前端开发怎么这么多花样”的感觉。那要怎么理解 Vue 框架提供的能力，这些能力又是怎样提高开发人员的开发效率和代码维护性的呢？</p> <p><strong>模板引擎</strong><br>
模板引擎大概是 Vue 里最主要、又最核心的一个能力。前面也讲到，在模板引擎还没有出现的时候，前端需要手动更新前端页面的内容，需要维护一大堆的 HTML 和变量拼接的动态内容，虽然 jQuery 的出现提升了 DOM 元素的操作性，但依然难以避免代码的可读性、可维护性上存在的一些问题。</p> <p>以前我们更新页面的内容，大概的流程是：监听操作 -&gt; 获取数据变量 -&gt; 使用数据拼接成 HTML 模板 -&gt; 将 HTML 内容塞到页面对应的地方 -&gt; 将 HTML 片段内需要监听的点击等事件进行绑定。</p> <p>这么复杂的逻辑，如今使用 Vue，就可以方便地在模板里用插值表达式<code>{{}}</code>、<code>v-bind</code>绑定变量来展示，同时配合<code>v-if</code>、<code>v-for</code>这些内置指令，就可以很方便地写出可读性和维护性都很不错的代码了。什么是插值表达式？什么是指令？这些我们会放在后面的章节里介绍。这里我们主要来介绍下 Vue 框架做了什么事情，这里先讲一下数据绑定。</p> <p>我们在 Vue 里渲染一块内容，一般会有以下流程：<br>
(1) 解析语法生成 AST。<br>
(2) 根据 AST 结果，完成 data 数据初始化。<br>
(3) 根据 AST 结果和 data 数据绑定情况，生成虚拟 DOM。<br>
(4) 将虚拟 DOM 生成真正的 DOM 插入到页面中，此时页面会被渲染。</p> <p>这样的过程要怎么去理解呢，我们一点点来看。</p> <h2 id="_1-1-解析语法生成-ast"><a href="#_1-1-解析语法生成-ast" aria-hidden="true" class="header-anchor">#</a> 1.1 解析语法生成 AST</h2> <p>抽象语法树（Abstract Syntax Tree）也称为 AST 语法树，指的是源代码语法所对应的树状结构。也就是说，对于一种具体编程语言下的源代码，通过构建语法树的形式将源代码中的语句映射到树中的每一个节点上。</p> <p>其实我们的 DOM 结构树，也是 AST 的一种，把 HTML DOM 语法解析并生成最终的页面。而模板引擎中常用的，则是将模板语法解析，分别生成 HTML DOM，使用像 HTML 拼接这样的方式（在对应的位置绑定变量、指令解析获取拼接逻辑等等），同时配合事件的管理、虚拟 DOM 的设计，可以最大化地提升页面的性能。</p> <p>我们仔细看看这个过程是怎样的。</p> <h3 id="_1-1-1-捕获特定语法"><a href="#_1-1-1-捕获特定语法" aria-hidden="true" class="header-anchor">#</a> 1.1.1 捕获特定语法</h3> <p>生成 AST 的过程涉及到编译器的原理，一般经过以下过程（参考维基百科）：</p> <p>(1) 语法分析。<br>
语法分析的任务是在词法分析的基础上将单词序列组合成各类语法短语，如“程序”，“语句”，“表达式”等等。语法分析程序判断源程序在结构上是否正确，源程序的结构由上下文无关文法描述。例如 Vue 框架中，有<code>v-if</code>/<code>v-for</code>这样的指令，也有<code>&lt;my-custom-component&gt;</code>这样的自定义 DOM 标签，还有<code>@click</code>/<code>:props</code>这样的简化绑定语法，模板引擎需要将它们一一解析出来，并相应地进行后续的处理。</p> <p>(2) 语义分析。<br>
语义分析是编译过程的一个逻辑阶段，语义分析的任务是对结构上正确的源程序进行上下文有关性质的审查，进行类型审查。语义分析是审查源程序有无语义错误，为代码生成阶段收集类型信息，一般类型检查也会在这个过程中进行。在 Vue 框架中，例如我们绑定了某个不存在的变量或者事件，又或者是使用了某个未定义的自定义组件等，都会在这个阶段进行报错提示。</p> <p>(3) 生成 AST。<br>
AST 的结构则根据使用者需要定义，1.1.2 节中生成的一些 AST 对象都是本人根据需要假设定义的，并不完全是 Vue 中最终的实现效果。</p> <p>关于编译器的更详细的原理，大家感兴趣可以翻看其他大佬们的著作，例如很经典的编译原理。Vue 里到底是怎样处理的呢，语法分析、语义分析基本上是通过正则的方式来处理，生成 AST 的部分我们可以看一下简化后的源码，其实就是将解析出来的元素、指令、属性、父子节点关系等内容进行处理，得到一个 AST 对象：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">/**
 *  将HTML编译成AST对象
 */</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">parse</span><span class="token punctuation">(</span>
  <span class="token parameter">template<span class="token punctuation">:</span> string<span class="token punctuation">,</span>
  options<span class="token punctuation">:</span> CompilerOptions</span>
<span class="token punctuation">)</span><span class="token punctuation">:</span> ASTElement <span class="token operator">|</span> <span class="token keyword">void</span> <span class="token punctuation">{</span>
  <span class="token comment">// 返回AST对象</span>
  <span class="token comment">// 篇幅原因，一些前置定义省略</span>
  <span class="token comment">// 此处开始解析HTML模板</span>
  <span class="token function">parseHTML</span><span class="token punctuation">(</span>template<span class="token punctuation">,</span> <span class="token punctuation">{</span>
    expectHTML<span class="token punctuation">:</span> options<span class="token punctuation">.</span>expectHTML<span class="token punctuation">,</span>
    isUnaryTag<span class="token punctuation">:</span> options<span class="token punctuation">.</span>isUnaryTag<span class="token punctuation">,</span>
    shouldDecodeNewlines<span class="token punctuation">:</span> options<span class="token punctuation">.</span>shouldDecodeNewlines<span class="token punctuation">,</span>
    <span class="token function">start</span><span class="token punctuation">(</span><span class="token parameter">tag<span class="token punctuation">,</span> attrs<span class="token punctuation">,</span> unary</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token comment">// 一些前置检查和设置、兼容处理此处省略</span>
      <span class="token comment">// 此处定义了初始化的元素AST对象</span>
      <span class="token keyword">const</span> element<span class="token punctuation">:</span> ASTElement <span class="token operator">=</span> <span class="token punctuation">{</span>
        type<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span>
        tag<span class="token punctuation">,</span>
        attrsList<span class="token punctuation">:</span> attrs<span class="token punctuation">,</span>
        attrsMap<span class="token punctuation">:</span> <span class="token function">makeAttrsMap</span><span class="token punctuation">(</span>attrs<span class="token punctuation">)</span><span class="token punctuation">,</span>
        parent<span class="token punctuation">:</span> currentParent<span class="token punctuation">,</span>
        children<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
      <span class="token punctuation">}</span><span class="token punctuation">;</span>
      <span class="token comment">// 检查元素标签是否合法（不是保留命名）</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isForbiddenTag</span><span class="token punctuation">(</span>element<span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span><span class="token function">isServerRendering</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        element<span class="token punctuation">.</span>forbidden <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
        process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">NODE_ENV</span> <span class="token operator">!==</span> <span class="token string">&quot;production&quot;</span> <span class="token operator">&amp;&amp;</span>
          <span class="token function">warn</span><span class="token punctuation">(</span>
            <span class="token string">&quot;Templates should only be responsible for mapping the state to the &quot;</span> <span class="token operator">+</span>
              <span class="token string">&quot;UI. Avoid placing tags with side-effects in your templates, such as &quot;</span> <span class="token operator">+</span>
              <span class="token template-string"><span class="token string">`&lt;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>tag<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&gt;`</span></span> <span class="token operator">+</span>
              <span class="token string">&quot;, as they will not be parsed.&quot;</span>
          <span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span>
      <span class="token comment">// 执行一些前置的元素预处理</span>
      <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> preTransforms<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        preTransforms<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">(</span>element<span class="token punctuation">,</span> options<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span>
      <span class="token comment">// 是否原生元素</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>inVPre<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment">// 处理元素元素的一些属性</span>
        <span class="token function">processRawAttrs</span><span class="token punctuation">(</span>element<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
        <span class="token comment">// 处理指令，此处包括v-for/v-if/v-once/key等等</span>
        <span class="token function">processFor</span><span class="token punctuation">(</span>element<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">processIf</span><span class="token punctuation">(</span>element<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">processOnce</span><span class="token punctuation">(</span>element<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">processKey</span><span class="token punctuation">(</span>element<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 删除结构属性</span>

        <span class="token comment">// 确定这是否是一个简单的元素</span>
        element<span class="token punctuation">.</span>plain <span class="token operator">=</span> <span class="token operator">!</span>element<span class="token punctuation">.</span>key <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span>attrs<span class="token punctuation">.</span>length<span class="token punctuation">;</span>

        <span class="token comment">// 处理ref/slot/component等属性</span>
        <span class="token function">processRef</span><span class="token punctuation">(</span>element<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">processSlot</span><span class="token punctuation">(</span>element<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">processComponent</span><span class="token punctuation">(</span>element<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> transforms<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
          transforms<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">(</span>element<span class="token punctuation">,</span> options<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token function">processAttrs</span><span class="token punctuation">(</span>element<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span>

      <span class="token comment">// 后面还有一些父子节点等处理，此处省略</span>
    <span class="token punctuation">}</span>
    <span class="token comment">// 其他省略</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> root<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre></div><h3 id="_1-1-2-dom-元素捕获"><a href="#_1-1-2-dom-元素捕获" aria-hidden="true" class="header-anchor">#</a> 1.1.2 DOM 元素捕获</h3> <p>最简单的，我们来捕获一个<code>&lt;div&gt;</code>元素，然后生成一个<code>&lt;div&gt;</code>元素。例如开发者写了以下这么一段模板，我们可以对它们进行捕获：</p> <div class="language-html extra-class"><pre class="language-html"><code><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span><span class="token punctuation">&gt;</span></span>123<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">&gt;</span></span>456<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span><span class="token punctuation">&gt;</span></span>789<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
</code></pre></div><p>捕获后我们或许可以得到这样的一个对象：</p> <div class="language-js extra-class"><pre class="language-js"><code>thisDiv <span class="token operator">=</span> <span class="token punctuation">{</span>
  dom<span class="token punctuation">:</span> <span class="token punctuation">{</span>
    type<span class="token punctuation">:</span> <span class="token string">&quot;dom&quot;</span><span class="token punctuation">,</span>
    ele<span class="token punctuation">:</span> <span class="token string">&quot;div&quot;</span><span class="token punctuation">,</span>
    nodeIndex<span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
    children<span class="token punctuation">:</span> <span class="token punctuation">[</span>
      <span class="token punctuation">{</span>
        type<span class="token punctuation">:</span> <span class="token string">&quot;dom&quot;</span><span class="token punctuation">,</span>
        ele<span class="token punctuation">:</span> <span class="token string">&quot;a&quot;</span><span class="token punctuation">,</span>
        nodeIndex<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span>
        children<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span> type<span class="token punctuation">:</span> <span class="token string">&quot;text&quot;</span><span class="token punctuation">,</span> value<span class="token punctuation">:</span> <span class="token string">&quot;123&quot;</span> <span class="token punctuation">}</span><span class="token punctuation">]</span>
      <span class="token punctuation">}</span><span class="token punctuation">,</span>
      <span class="token punctuation">{</span>
        type<span class="token punctuation">:</span> <span class="token string">&quot;dom&quot;</span><span class="token punctuation">,</span>
        ele<span class="token punctuation">:</span> <span class="token string">&quot;p&quot;</span><span class="token punctuation">,</span>
        nodeIndex<span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">,</span>
        children<span class="token punctuation">:</span> <span class="token punctuation">[</span>
          <span class="token punctuation">{</span> type<span class="token punctuation">:</span> <span class="token string">&quot;text&quot;</span><span class="token punctuation">,</span> value<span class="token punctuation">:</span> <span class="token string">&quot;456&quot;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
          <span class="token punctuation">{</span>
            type<span class="token punctuation">:</span> <span class="token string">&quot;dom&quot;</span><span class="token punctuation">,</span>
            ele<span class="token punctuation">:</span> <span class="token string">&quot;span&quot;</span><span class="token punctuation">,</span>
            nodeIndex<span class="token punctuation">:</span> <span class="token number">3</span><span class="token punctuation">,</span>
            children<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span> type<span class="token punctuation">:</span> <span class="token string">&quot;text&quot;</span><span class="token punctuation">,</span> value<span class="token punctuation">:</span> <span class="token string">&quot;789&quot;</span> <span class="token punctuation">}</span><span class="token punctuation">]</span>
          <span class="token punctuation">}</span>
        <span class="token punctuation">]</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">]</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre></div><p>这个对象维护我们需要的一些信息，某个 HTML 元素里需要绑定哪些变量（变量更新的时候需要更新该节点内容），以怎样的方式来拼接（是否有逻辑指令，如<code>v-if</code>、<code>v-for</code>等），哪些节点绑定了怎样的事件监听事件（是否匹配一些常用的事件能力支持），所以这里 AST 能做的事情是很多了。</p> <p>我们最终还是会根据 AST 对象生成真实的页面片段和逻辑，实现过程其实也是将很多的特殊标识（例如元素 ID、属性标记等）打到该元素上，同时配合一些 Javascript 的元素选择方式、事件监听方式等，来将这个元素动态化（支持内容更新、节点更新）而实现最终的页面效果。Vue 会根据 AST 对象生成一段可执行的代码，我们看看这部分的实现：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 生成一个元素</span>
<span class="token keyword">function</span> <span class="token function">genElement</span><span class="token punctuation">(</span><span class="token parameter">el<span class="token punctuation">:</span> ASTElement</span><span class="token punctuation">)</span><span class="token punctuation">:</span> string <span class="token punctuation">{</span>
  <span class="token comment">// 根据该元素是否有相关的指令、属性语法对象，来进行对应的代码生成</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>el<span class="token punctuation">.</span>staticRoot <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span>el<span class="token punctuation">.</span>staticProcessed<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">genStatic</span><span class="token punctuation">(</span>el<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>el<span class="token punctuation">.</span>once <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span>el<span class="token punctuation">.</span>onceProcessed<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">genOnce</span><span class="token punctuation">(</span>el<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>el<span class="token punctuation">.</span>for <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span>el<span class="token punctuation">.</span>forProcessed<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">genFor</span><span class="token punctuation">(</span>el<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>el<span class="token punctuation">.</span>if <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span>el<span class="token punctuation">.</span>ifProcessed<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">genIf</span><span class="token punctuation">(</span>el<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>el<span class="token punctuation">.</span>tag <span class="token operator">===</span> <span class="token string">&quot;template&quot;</span> <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span>el<span class="token punctuation">.</span>slotTarget<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">genChildren</span><span class="token punctuation">(</span>el<span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token string">&quot;void 0&quot;</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>el<span class="token punctuation">.</span>tag <span class="token operator">===</span> <span class="token string">&quot;slot&quot;</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">genSlot</span><span class="token punctuation">(</span>el<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
    <span class="token comment">// component或者element的代码生成</span>
    <span class="token keyword">let</span> code<span class="token punctuation">;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>el<span class="token punctuation">.</span>component<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      code <span class="token operator">=</span> <span class="token function">genComponent</span><span class="token punctuation">(</span>el<span class="token punctuation">.</span>component<span class="token punctuation">,</span> el<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
      <span class="token keyword">const</span> data <span class="token operator">=</span> el<span class="token punctuation">.</span>plain <span class="token operator">?</span> <span class="token keyword">undefined</span> <span class="token punctuation">:</span> <span class="token function">genData</span><span class="token punctuation">(</span>el<span class="token punctuation">)</span><span class="token punctuation">;</span>

      <span class="token keyword">const</span> children <span class="token operator">=</span> el<span class="token punctuation">.</span>inlineTemplate <span class="token operator">?</span> <span class="token keyword">null</span> <span class="token punctuation">:</span> <span class="token function">genChildren</span><span class="token punctuation">(</span>el<span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      code <span class="token operator">=</span> <span class="token template-string"><span class="token string">`_c('</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>el<span class="token punctuation">.</span>tag<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">'</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>
        data <span class="token operator">?</span> `<span class="token punctuation">,</span>$<span class="token punctuation">{</span>data<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">`</span></span> <span class="token punctuation">:</span> <span class="token string">&quot;&quot;</span> <span class="token comment">// data</span>
      <span class="token punctuation">}</span>$<span class="token punctuation">{</span>
        children <span class="token operator">?</span> <span class="token template-string"><span class="token string">`,</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>children<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">`</span></span> <span class="token punctuation">:</span> <span class="token string">&quot;&quot;</span> <span class="token comment">// children</span>
      <span class="token punctuation">}</span><span class="token punctuation">)</span>`<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token comment">// 模块转换</span>
    <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> transforms<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      code <span class="token operator">=</span> transforms<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">(</span>el<span class="token punctuation">,</span> code<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token comment">// 返回最后拼装好的可执行的代码</span>
    <span class="token keyword">return</span> code<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><h3 id="_1-1-3-模板引擎赋能"><a href="#_1-1-3-模板引擎赋能" aria-hidden="true" class="header-anchor">#</a> 1.1.3 模板引擎赋能</h3> <p>原本就是一个<code>&lt;div&gt;</code>，经过 AST 生成一个对象，最终还是生成一个<code>&lt;div&gt;</code>，这是多余的步骤吗？不是的，在这个过程中我们可以实现一些功能：<br>
(1) 排除无效 DOM 元素，并在构建过程可进行报错。<br>
(2) 使用自定义组件的时候，可匹配出来。<br>
(3) 可方便地实现数据绑定、事件绑定等功能。<br>
(4) 为虚拟 DOM Diff 过程打下铺垫。<br>
(5) HTML 转义（预防 XSS 漏洞）。</p> <p>通过通用的模板引擎，我们能处理掉很多低效又重复的工作，例如浏览器兼容、全局事件的统一管理和维护、模板更新的虚拟 DOM 机制、树状组织管理组件，而留给开发者的事情就可以少之又少。现在我们知道了模板引擎都做了什么事情，就可以区分开 Vue 框架提供的能力和我们需要自行处理的逻辑，可以更专注于业务开发。</p> <p>我们来看看虚拟 DOM 的机制。</p> <h2 id="_1-2-虚拟-dom"><a href="#_1-2-虚拟-dom" aria-hidden="true" class="header-anchor">#</a> 1.2 虚拟 DOM</h2> <p>虚拟 DOM 如今都被作为前端面试基础题库之一了，多多少少面试者都要去了解下，当初 React 就是因为虚拟 DOM 的提出，暂时赢下了浏览器性能之争（尤其在 AngularJS 的脏检查机制对比下）。当然，这都是几年前的事情了，如今几大框架的性能问题，早也不是什么大问题了。</p> <p>虚拟 DOM 大概可分成三个过程：<br>
(1) 用 JS 对象模拟 DOM 树，得到一棵虚拟 DOM 树。<br>
(2) 当页面数据变更时，生成新的虚拟 DOM 树，比较新旧两棵虚拟 DOM 树的差异。<br>
(3) 把差异应用到真正的 DOM 树上。</p> <h3 id="_1-2-1-用-js-对象模拟-dom-树"><a href="#_1-2-1-用-js-对象模拟-dom-树" aria-hidden="true" class="header-anchor">#</a> 1.2.1 用 JS 对象模拟 DOM 树</h3> <p>为什么要用到虚拟 DOM 呢？不知道大家仔细研究过 DOM 节点对象没，一个真正的 DOM 元素非常庞大，拥有很多的属性值。因为一个 DOM 节点它包括了太多太多的属性、元素和事件对象，实际上我们并不是全部都会用到，通常包括节点内容、元素位置、样式、节点的添加删除等方法。所以，我们通过用 JS 对象表示 DOM 元素的方式，可以大大降低了比较差异的计算量。</p> <p>看一下 Vue 源码，我们会发现 VNode 只有这些属性：</p> <div class="language-js extra-class"><pre class="language-js"><code>tag<span class="token punctuation">:</span> string <span class="token operator">|</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
data<span class="token punctuation">:</span> VNodeData <span class="token operator">|</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
children<span class="token punctuation">:</span> <span class="token operator">?</span>Array<span class="token operator">&lt;</span>VNode<span class="token operator">&gt;</span><span class="token punctuation">;</span>
text<span class="token punctuation">:</span> string <span class="token operator">|</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
elm<span class="token punctuation">:</span> Node <span class="token operator">|</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
ns<span class="token punctuation">:</span> string <span class="token operator">|</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
context<span class="token punctuation">:</span> Component <span class="token operator">|</span> <span class="token keyword">void</span><span class="token punctuation">;</span> <span class="token comment">// rendered in this component's scope</span>
key<span class="token punctuation">:</span> string <span class="token operator">|</span> number <span class="token operator">|</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
componentOptions<span class="token punctuation">:</span> VNodeComponentOptions <span class="token operator">|</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
componentInstance<span class="token punctuation">:</span> Component <span class="token operator">|</span> <span class="token keyword">void</span><span class="token punctuation">;</span> <span class="token comment">// component instance</span>
parent<span class="token punctuation">:</span> VNode <span class="token operator">|</span> <span class="token keyword">void</span><span class="token punctuation">;</span> <span class="token comment">// component placeholder node</span>
<span class="token comment">// strictly internal</span>
raw<span class="token punctuation">:</span> boolean<span class="token punctuation">;</span> <span class="token comment">// contains raw HTML? (server only)</span>
isStatic<span class="token punctuation">:</span> boolean<span class="token punctuation">;</span> <span class="token comment">// hoisted static node</span>
isRootInsert<span class="token punctuation">:</span> boolean<span class="token punctuation">;</span> <span class="token comment">// necessary for enter transition check</span>
isComment<span class="token punctuation">:</span> boolean<span class="token punctuation">;</span> <span class="token comment">// empty comment placeholder?</span>
isCloned<span class="token punctuation">:</span> boolean<span class="token punctuation">;</span> <span class="token comment">// is a cloned node?</span>
isOnce<span class="token punctuation">:</span> boolean<span class="token punctuation">;</span> <span class="token comment">// is a v-once node?</span>
asyncFactory<span class="token punctuation">:</span> Function <span class="token operator">|</span> <span class="token keyword">void</span><span class="token punctuation">;</span> <span class="token comment">// async component factory function</span>
asyncMeta<span class="token punctuation">:</span> Object <span class="token operator">|</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
isAsyncPlaceholder<span class="token punctuation">:</span> boolean<span class="token punctuation">;</span>
ssrContext<span class="token punctuation">:</span> Object <span class="token operator">|</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
fnContext<span class="token punctuation">:</span> Component <span class="token operator">|</span> <span class="token keyword">void</span><span class="token punctuation">;</span> <span class="token comment">// real context vm for functional nodes</span>
fnOptions<span class="token punctuation">:</span> <span class="token operator">?</span>ComponentOptions<span class="token punctuation">;</span> <span class="token comment">// for SSR caching</span>
devtoolsMeta<span class="token punctuation">:</span> <span class="token operator">?</span>Object<span class="token punctuation">;</span> <span class="token comment">// used to store functional render context fordevtools</span>
fnScopeId<span class="token punctuation">:</span> <span class="token operator">?</span>string<span class="token punctuation">;</span> <span class="token comment">// functional scope id support</span>
</code></pre></div><h3 id="_1-2-2-比较新旧两棵虚拟-dom-树的差异"><a href="#_1-2-2-比较新旧两棵虚拟-dom-树的差异" aria-hidden="true" class="header-anchor">#</a> 1.2.2 比较新旧两棵虚拟 DOM 树的差异</h3> <p>虚拟 DOM 中，差异对比也是很关键的一步，这里简单说一下。当状态变更的时候，重新构造一棵新的对象树。然后用新的树和旧的树进行比较，记录两棵树差异。通常来说这样的差异需要记录：</p> <ul><li>需要替换掉原来的节点</li> <li>移动、删除、新增子节点</li> <li>修改了节点的属性</li> <li>对于文本节点的文本内容改变</li></ul> <p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/wxapp-4.png" alt=""><br>
图 1-1 DOM 树对比</p> <p>如图 1-1，这里我们对比两棵 DOM 树，得到的差异有：p 元素插入了一个 span 元素子节点，然后原先的文本节点挪到了 span 元素子节点下面。最后通过 JS 操作就可以实现完，具体实现的步骤则是第三步：</p> <h3 id="_1-2-3-把差异应用到真正的-dom-树上"><a href="#_1-2-3-把差异应用到真正的-dom-树上" aria-hidden="true" class="header-anchor">#</a> 1.2.3 把差异应用到真正的 DOM 树上</h3> <p>经过差异对比之后，我们能获得一组差异记录，接下里我们需要使用它。差异记录要应用到真正的 DOM 树上，需要进行一些操作，例如节点的替换、移动、删除，文本内容的改变等。</p> <p>我们来看一下，在 Vue 中是怎么进行 DOM Diff 的，虽然代码里很多函数没贴出来，但其实看函数名也可以大概理解都是什么作用，例如<code>updateChildren</code>、<code>addVnodes</code>、<code>removeVnodes</code>、<code>setTextContent</code>等，大致是个对比差异然后更新的操作：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 简单看这段代码感受下</span>
<span class="token comment">// 对比差异然后更新</span>
<span class="token keyword">const</span> oldCh <span class="token operator">=</span> oldVnode<span class="token punctuation">.</span>children<span class="token punctuation">;</span>
<span class="token keyword">const</span> ch <span class="token operator">=</span> vnode<span class="token punctuation">.</span>children<span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isDef</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token function">isPatchable</span><span class="token punctuation">(</span>vnode<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">for</span> <span class="token punctuation">(</span>i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> cbs<span class="token punctuation">.</span>update<span class="token punctuation">.</span>length<span class="token punctuation">;</span> <span class="token operator">++</span>i<span class="token punctuation">)</span> cbs<span class="token punctuation">.</span>update<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">(</span>oldVnode<span class="token punctuation">,</span> vnode<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isDef</span><span class="token punctuation">(</span><span class="token punctuation">(</span>i <span class="token operator">=</span> data<span class="token punctuation">.</span>hook<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token function">isDef</span><span class="token punctuation">(</span><span class="token punctuation">(</span>i <span class="token operator">=</span> i<span class="token punctuation">.</span>update<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token function">i</span><span class="token punctuation">(</span>oldVnode<span class="token punctuation">,</span> vnode<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isUndef</span><span class="token punctuation">(</span>vnode<span class="token punctuation">.</span>text<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isDef</span><span class="token punctuation">(</span>oldCh<span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token function">isDef</span><span class="token punctuation">(</span>ch<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>oldCh <span class="token operator">!==</span> ch<span class="token punctuation">)</span>
      <span class="token function">updateChildren</span><span class="token punctuation">(</span>elm<span class="token punctuation">,</span> oldCh<span class="token punctuation">,</span> ch<span class="token punctuation">,</span> insertedVnodeQueue<span class="token punctuation">,</span> removeOnly<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isDef</span><span class="token punctuation">(</span>ch<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">NODE_ENV</span> <span class="token operator">!==</span> <span class="token string">&quot;production&quot;</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token function">checkDuplicateKeys</span><span class="token punctuation">(</span>ch<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isDef</span><span class="token punctuation">(</span>oldVnode<span class="token punctuation">.</span>text<span class="token punctuation">)</span><span class="token punctuation">)</span> nodeOps<span class="token punctuation">.</span><span class="token function">setTextContent</span><span class="token punctuation">(</span>elm<span class="token punctuation">,</span> <span class="token string">&quot;&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token function">addVnodes</span><span class="token punctuation">(</span>elm<span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> ch<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> ch<span class="token punctuation">.</span>length <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">,</span> insertedVnodeQueue<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isDef</span><span class="token punctuation">(</span>oldCh<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token function">removeVnodes</span><span class="token punctuation">(</span>elm<span class="token punctuation">,</span> oldCh<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> oldCh<span class="token punctuation">.</span>length <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isDef</span><span class="token punctuation">(</span>oldVnode<span class="token punctuation">.</span>text<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    nodeOps<span class="token punctuation">.</span><span class="token function">setTextContent</span><span class="token punctuation">(</span>elm<span class="token punctuation">,</span> <span class="token string">&quot;&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>oldVnode<span class="token punctuation">.</span>text <span class="token operator">!==</span> vnode<span class="token punctuation">.</span>text<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  nodeOps<span class="token punctuation">.</span><span class="token function">setTextContent</span><span class="token punctuation">(</span>elm<span class="token punctuation">,</span> vnode<span class="token punctuation">.</span>text<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isDef</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isDef</span><span class="token punctuation">(</span><span class="token punctuation">(</span>i <span class="token operator">=</span> data<span class="token punctuation">.</span>hook<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token function">isDef</span><span class="token punctuation">(</span><span class="token punctuation">(</span>i <span class="token operator">=</span> i<span class="token punctuation">.</span>postpatch<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token function">i</span><span class="token punctuation">(</span>oldVnode<span class="token punctuation">,</span> vnode<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre></div><p>前言中我们提到，页面的更新有两种方式：字符串模板和节点模板。而字符串模板<code>innerHTML</code>的方式会消耗比较多的计算，节点模板的方式又需要管理特别多的节点信息和引用保存，使用虚拟 DOM 则可以有效地降低浏览器计算和性能。</p> <h2 id="_1-3-数据绑定"><a href="#_1-3-数据绑定" aria-hidden="true" class="header-anchor">#</a> 1.3 数据绑定</h2> <p>在 Vue 中，最基础的模板语法是数据绑定，例如：</p> <div class="language-html extra-class"><pre class="language-html"><code><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">&gt;</span></span>{{ message }}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
</code></pre></div><p>这里使用插值表达式<code>{{}}</code>绑定了一个<code>message</code>的变量，开发者在 Vue 实例<code>data</code>中绑定该变量：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">new</span> <span class="token class-name">Vue</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  data<span class="token punctuation">:</span> <span class="token punctuation">{</span>
    message<span class="token punctuation">:</span> <span class="token string">&quot;测试文本&quot;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><p>最终页面展示内容为<code>&lt;div&gt;测试文本&lt;/div&gt;</code>。这是怎么做到的呢？</p> <h3 id="_1-3-1-数据绑定的实现"><a href="#_1-3-1-数据绑定的实现" aria-hidden="true" class="header-anchor">#</a> 1.3.1 数据绑定的实现</h3> <p>这种使用双大括号来绑定变量的方式，我们称之为数据绑定。它是怎么实现的呢，前面也有大致提到，数据绑定的过程其实不复杂：<br>
(1) 解析语法生成 AST。<br>
(2) 根据 AST 结果生成 DOM。<br>
(3) 将数据绑定更新至模板。</p> <p>上述这个过程，是 Vue 中模板引擎在做的事情。我们来看看上面在 Vue 里的代码片段<code>&lt;div&gt;&lt;/div&gt;</code>，我们可以通过 DOM 元素捕获，解析后获得这样一个 AST 对象：</p> <div class="language-js extra-class"><pre class="language-js"><code>thisDiv <span class="token operator">=</span> <span class="token punctuation">{</span>
  dom<span class="token punctuation">:</span> <span class="token punctuation">{</span>
    type<span class="token punctuation">:</span> <span class="token string">&quot;dom&quot;</span><span class="token punctuation">,</span>
    ele<span class="token punctuation">:</span> <span class="token string">&quot;div&quot;</span><span class="token punctuation">,</span>
    nodeIndex<span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
    children<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span> type<span class="token punctuation">:</span> <span class="token string">&quot;text&quot;</span><span class="token punctuation">,</span> value<span class="token punctuation">:</span> <span class="token string">&quot;&quot;</span> <span class="token punctuation">}</span><span class="token punctuation">]</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  binding<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span> type<span class="token punctuation">:</span> <span class="token string">&quot;dom&quot;</span><span class="token punctuation">,</span> nodeIndex<span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">,</span> valueName<span class="token punctuation">:</span> <span class="token string">&quot;message&quot;</span> <span class="token punctuation">}</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre></div><p>这样，我们在生成一个 DOM 的时候，同时添加对<code>message</code>的监听，数据更新时我们会找到对应的<code>nodeIndex</code>，更新值：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 假设这是一个生成 DOM 的过程，包括 innerHTML 和事件监听</span>
<span class="token keyword">function</span> <span class="token function">generateDOM</span><span class="token punctuation">(</span><span class="token parameter">astObject</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">{</span> dom<span class="token punctuation">,</span> binding <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">}</span> <span class="token operator">=</span> astObject<span class="token punctuation">;</span>
  <span class="token comment">// 生成DOM，这里假装当前节点是baseDom</span>
  baseDom<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> <span class="token function">getDOMString</span><span class="token punctuation">(</span>dom<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token comment">// 对于数据绑定的，来进行监听更新吧</span>
  baseDom<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">&quot;data:change&quot;</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">name<span class="token punctuation">,</span> value</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token comment">// 寻找匹配的数据绑定</span>
    <span class="token keyword">const</span> obj <span class="token operator">=</span> binding<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token parameter">x</span> <span class="token operator">=&gt;</span> x<span class="token punctuation">.</span>valueName <span class="token operator">==</span> name<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 若找到值绑定的对应节点，则更新其值。</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>obj<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      baseDom<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token template-string"><span class="token string">`[data-node-index=&quot;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>obj<span class="token punctuation">.</span>nodeIndex<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&quot;]`</span></span><span class="token punctuation">)</span><span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> value<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token comment">// 获取DOM字符串，这里简单拼成字符串</span>
<span class="token keyword">function</span> <span class="token function">getDOMString</span><span class="token punctuation">(</span><span class="token parameter">domObj</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// 无效对象返回''</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>domObj<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token string">&quot;&quot;</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> <span class="token punctuation">{</span> type<span class="token punctuation">,</span> children <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> nodeIndex<span class="token punctuation">,</span> ele<span class="token punctuation">,</span> value <span class="token punctuation">}</span> <span class="token operator">=</span> domObj<span class="token punctuation">;</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>type <span class="token operator">==</span> <span class="token string">&quot;dom&quot;</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 若有子对象，递归返回生成的字符串拼接</span>
    <span class="token keyword">const</span> childString <span class="token operator">=</span> <span class="token string">&quot;&quot;</span><span class="token punctuation">;</span>
    children<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">x</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      childString <span class="token operator">+=</span> <span class="token function">getDOMString</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// dom对象，拼接生成对象字符串</span>
    <span class="token keyword">return</span> <span class="token template-string"><span class="token string">`&lt;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>ele<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> data-node-index=&quot;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>nodeIndex<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&quot;&gt;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>childString<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&lt;/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>ele<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&gt;`</span></span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>type <span class="token operator">==</span> <span class="token string">&quot;text&quot;</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 若为textNode，返回text的值</span>
    <span class="token keyword">return</span> value<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p>这样，我们就能在<code>message</code>变量更新的时候，通过该变量关联的引用，来自动更新对应展示的内容。而要知道<code>message</code>变量什么时候进行了改变，我们需要对数据进行监听。</p> <h3 id="_1-3-2-数据更新监听"><a href="#_1-3-2-数据更新监听" aria-hidden="true" class="header-anchor">#</a> 1.3.2 数据更新监听</h3> <p>我们能看到，上面的简单代码描述过程中，使用的数据监听方法是用了<code>addEventListener(&quot;data:change&quot;, Function)</code>的方式。其实每个框架都会有自己的方式，例如 Angular 使用的则是大家都熟知的脏检查。</p> <p><strong>(1) Angular: watch 机制。</strong>
脏检查是什么呢？在 AngularJS 中，并不直接监听数据的变动，而是监听常见的事件如用户交互（点击、输入等）、定时器、生命周期等。在每次事件触发完毕后，计算数据的新值和旧值是否有差异，若有差异则更新页面，并触发下一次的脏检查，直到没有差异或是次数达到设定阈值。很显然，这样每一次脏检查可能会形成环状，导致死循环。虽然 AngularJS 有阈值控制，但也无法避免这种机制所导致的低效甚至性能问题。</p> <p>由于设计上存在的这些问题一直被大家诟病，在 Angular2 中应用的组织类似 DOM，也是树结构的，脏检查会从根组件开始，自上而下对树上的所有子组件进行检查。相比 AngularJS 中的带有环的结构，这样的单向数据流效率更高，而且容易预测，性能上也有不少的提升。</p> <p><strong>(2) React: 手动 set 写入。</strong>
在 React 里，是需要手动调用<code>set()</code>的方式写入数据来更新模版。</p> <p><strong>(3) Vue: Getter/Setter。</strong>
而在 Vue 中，主要是依赖了<code>Getter/Setter</code>:</p> <div class="language-js extra-class"><pre class="language-js"><code>Object<span class="token punctuation">.</span><span class="token function">defineProperty</span><span class="token punctuation">(</span>obj<span class="token punctuation">,</span> key<span class="token punctuation">,</span> <span class="token punctuation">{</span>
  enumerable<span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
  configurable<span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
  <span class="token comment">// getter</span>
  <span class="token function-variable function">get</span><span class="token punctuation">:</span> <span class="token keyword">function</span> <span class="token function">reactiveGetter</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> value <span class="token operator">=</span> getter <span class="token operator">?</span> <span class="token function">getter</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span> <span class="token punctuation">:</span> val<span class="token punctuation">;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>Dep<span class="token punctuation">.</span>target<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      dep<span class="token punctuation">.</span><span class="token function">depend</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>childOb<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        childOb<span class="token punctuation">.</span>dep<span class="token punctuation">.</span><span class="token function">depend</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>Array<span class="token punctuation">.</span><span class="token function">isArray</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
          <span class="token function">dependArray</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">return</span> value<span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token comment">// setter，最终更新后会通知噢</span>
  <span class="token function-variable function">set</span><span class="token punctuation">:</span> <span class="token keyword">function</span> <span class="token function">reactiveSetter</span><span class="token punctuation">(</span><span class="token parameter">newVal</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> value <span class="token operator">=</span> getter <span class="token operator">?</span> <span class="token function">getter</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span> <span class="token punctuation">:</span> val<span class="token punctuation">;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>newVal <span class="token operator">===</span> value <span class="token operator">||</span> <span class="token punctuation">(</span>newVal <span class="token operator">!==</span> newVal <span class="token operator">&amp;&amp;</span> value <span class="token operator">!==</span> value<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">return</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">NODE_ENV</span> <span class="token operator">!==</span> <span class="token string">&quot;production&quot;</span> <span class="token operator">&amp;&amp;</span> customSetter<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token function">customSetter</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>getter <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span>setter<span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>setter<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token function">setter</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>obj<span class="token punctuation">,</span> newVal<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
      val <span class="token operator">=</span> newVal<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    childOb <span class="token operator">=</span> <span class="token operator">!</span>shallow <span class="token operator">&amp;&amp;</span> <span class="token function">observe</span><span class="token punctuation">(</span>newVal<span class="token punctuation">)</span><span class="token punctuation">;</span>
    dep<span class="token punctuation">.</span><span class="token function">notify</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><p>在数据更新的时候就执行了模板更新、watch、computed 等一些工作，而 Vue3.0 将使用<code>Proxy</code>的方式来进行，可参考<a href="/vue-ebook/vue-ebook/16.html">《第16章 关于 Vue 3.0》</a>。</p> <h2 id="_1-4-xss-漏洞填补"><a href="#_1-4-xss-漏洞填补" aria-hidden="true" class="header-anchor">#</a> 1.4 XSS 漏洞填补</h2> <p>模板引擎还可以协助预防下 XSS 相关漏洞。我们知道，XSS 的整个攻击过程大概为：<br>
(1) 通常页面中包含的用户输入内容都在固定的容器或者属性内，以文本的形式展示。<br>
(2) 攻击者利用这些页面的用户输入片段，拼接特殊格式的字符串，突破原有位置的限制，形成了代码片段。<br>
(3) 攻击者通过在目标网站上注入脚本，使之在用户的浏览器上运行，从而引发潜在风险。</p> <p>避免 XSS 的方法之一主要是将用户所提供的内容进行过滤，而大多数模板引擎会自带 HTML 转义功能。在 Vue 中，默认的数据绑定方式（双大括号、<code>v-bind</code>等）会进行 HTML 转义，将数据解释为普通文本，而非 HTML 代码。来感受下这堆正则和转义，篇幅关系，只贴一部分来观摩下：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 元素标签和属性相关的正则表达式</span>
<span class="token keyword">const</span> attribute <span class="token operator">=</span> <span class="token regex">/^\s*([^\s&quot;'&lt;&gt;\/=]+)(?:\s*(=)\s*(?:&quot;([^&quot;]*)&quot;+|'([^']*)'+|([^\s&quot;'=&lt;&gt;`]+)))?/</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> dynamicArgAttribute <span class="token operator">=</span> <span class="token regex">/^\s*((?:v-[\w-]+:|@|:|#)\[[^=]+\][^\s&quot;'&lt;&gt;\/=]*)(?:\s*(=)\s*(?:&quot;([^&quot;]*)&quot;+|'([^']*)'+|([^\s&quot;'=&lt;&gt;`]+)))?/</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> ncname <span class="token operator">=</span> <span class="token template-string"><span class="token string">`[a-zA-Z_][\\-\\.0-9_a-zA-Z</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>unicodeRegExp<span class="token punctuation">.</span>source<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">]*`</span></span><span class="token punctuation">;</span>
<span class="token keyword">const</span> qnameCapture <span class="token operator">=</span> <span class="token template-string"><span class="token string">`((?:</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>ncname<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">\\:)?</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>ncname<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">)`</span></span><span class="token punctuation">;</span>
<span class="token keyword">const</span> startTagOpen <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">RegExp</span><span class="token punctuation">(</span><span class="token template-string"><span class="token string">`^&lt;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>qnameCapture<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> startTagClose <span class="token operator">=</span> <span class="token regex">/^\s*(\/?)&gt;/</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> endTag <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">RegExp</span><span class="token punctuation">(</span><span class="token template-string"><span class="token string">`^&lt;\\/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>qnameCapture<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">[^&gt;]*&gt;`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> doctype <span class="token operator">=</span> <span class="token regex">/^&lt;!DOCTYPE [^&gt;]+&gt;/i</span><span class="token punctuation">;</span>
<span class="token comment">// 避免注释中被处理掉</span>
<span class="token keyword">const</span> comment <span class="token operator">=</span> <span class="token regex">/^&lt;!\--/</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> conditionalComment <span class="token operator">=</span> <span class="token regex">/^&lt;!\[/</span><span class="token punctuation">;</span>

<span class="token comment">// 特殊元素处理</span>
<span class="token keyword">export</span> <span class="token keyword">const</span> isPlainTextElement <span class="token operator">=</span> <span class="token function">makeMap</span><span class="token punctuation">(</span><span class="token string">&quot;script,style,textarea&quot;</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> reCache <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token comment">// 转义</span>
<span class="token keyword">const</span> decodingMap <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token string">&quot;&amp;lt;&quot;</span><span class="token punctuation">:</span> <span class="token string">&quot;&lt;&quot;</span><span class="token punctuation">,</span>
  <span class="token string">&quot;&amp;gt;&quot;</span><span class="token punctuation">:</span> <span class="token string">&quot;&gt;&quot;</span><span class="token punctuation">,</span>
  <span class="token string">&quot;&amp;quot;&quot;</span><span class="token punctuation">:</span> <span class="token string">'&quot;'</span><span class="token punctuation">,</span>
  <span class="token string">&quot;&amp;amp;&quot;</span><span class="token punctuation">:</span> <span class="token string">&quot;&amp;&quot;</span><span class="token punctuation">,</span>
  <span class="token string">&quot;&amp;#10;&quot;</span><span class="token punctuation">:</span> <span class="token string">&quot;\n&quot;</span><span class="token punctuation">,</span>
  <span class="token string">&quot;&amp;#9;&quot;</span><span class="token punctuation">:</span> <span class="token string">&quot;\t&quot;</span><span class="token punctuation">,</span>
  <span class="token string">&quot;&amp;#39;&quot;</span><span class="token punctuation">:</span> <span class="token string">&quot;'&quot;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre></div><p>当然，如果你一定要输出 HTML 代码，也可以使用<code>v-html</code>指令输出。官方文档也有特殊说明：你的站点上动态渲染的任意 HTML 可能会非常危险，因为它很容易导致 XSS 攻击。请只对可信内容使用 HTML 插值，绝不要对用户提供的内容使用插值。</p> <p>Vue 中大多数能力都依赖于模板引擎，包括组件化管理、事件管理、Vue 实例、生命周期等等，更多的能力阅读，大家感兴趣的可以去翻一下 Vue 源码，相信你理解了 AST、虚拟 DOM、数据绑定相关的机制之后，再去翻阅源码看具体的实现也不再是难事了。</p></div> <!----> <footer class="page-edit"><div class="edit-link"><a href="https://github.com/godbasin/vue-ebook/edit/ebook-sourcecode/docs/vue-ebook/1.md" target="_blank" rel="noopener noreferrer">帮阿猪改善此页面！</a> <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></div> <!----> <div style="margin-top:30px;"><div class="el-row" style="margin-left:-10px;margin-right:-10px;"><div class="el-col el-col-24 el-col-sm-0 el-col-md-2 el-col-lg-4" style="padding-left:10px;padding-right:10px;display:block;"><div style="width:1px;height:1px;"></div></div> <div class="el-col el-col-24 el-col-sm-24 el-col-md-18 el-col-lg-16" style="padding-left:10px;padding-right:10px;"><div class="el-card box-card is-always-shadow"><div class="el-card__header"><div class="clearfix"><span>温馨提示喵</span></div></div><div class="el-card__body"> <div class="el-row" style="margin-left:-10px;margin-right:-10px;"><div class="el-col el-col-24 el-col-xs-24 el-col-sm-12" style="padding-left:10px;padding-right:10px;"><div class="el-image"><div class="image-slot"><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/assets/img/loading.gif" style="width:100%;"></div><!----></div></div> <div class="el-col el-col-24 el-col-xs-24 el-col-sm-12" style="padding-left:10px;padding-right:10px;"><div class="copyright-text"><div>本书采用“保持署名—非商用”<a href="http://creativecommons.org/licenses/by-nc/4.0/" target="_blank">创意共享4.0许可证</a>。只要保持原作者署名和非商用，您可以自由地阅读、分享、修改本书。</div> <div>作者：<a href="https://github.com/godbasin" target="_blank">被删</a></div></div></div></div></div></div></div></div></div></footer> <div class="page-nav"><p class="inner"><span class="prev">
        ←
        <a href="/vue-ebook/vue-ebook/0.html" class="prev">
          前端框架的出现
        </a></span> <span class="next"><a href="/vue-ebook/vue-ebook/2.html">
          第2章 Vue 环境快速搭建
        </a>
        →
      </span></p></div>  <div class="gitalk-container theme-default-content"><div id="gitalk-container" class="content"></div></div></main> <div id="kitty-container"><span><div role="tooltip" id="el-popover-1132" aria-hidden="true" class="el-popover el-popper" style="width:undefinedpx;display:none;"><!----><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/2code2.jpg" class="image"> <div class="text">牡羊猪的猫粮罐</div> </div><div id="kitty" style="background:url(https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/assets/img/kitty1.svg);"></div></span> <div class="el-dialog__wrapper" style="display:none;"><div role="dialog" aria-modal="true" aria-label="牡羊猪是这样渐渐胖成猪的喵（点击图片可以切换噢）" class="el-dialog" style="margin-top:15vh;"><div class="el-dialog__header"><span class="el-dialog__title">牡羊猪是这样渐渐胖成猪的喵（点击图片可以切换噢）</span><button type="button" aria-label="Close" class="el-dialog__headerbtn"><i class="el-dialog__close el-icon el-icon-close"></i></button></div><!----><!----></div></div></div></div><div class="global-ui"></div></div>
    <script src="/vue-ebook/assets/js/app.0a4e61a5.js" defer></script><script src="/vue-ebook/assets/js/3.846561a8.js" defer></script><script src="/vue-ebook/assets/js/2.eed49fdd.js" defer></script><script src="/vue-ebook/assets/js/8.0e937857.js" defer></script>
  </body>
</html>
